from config import Config
from functools import wraps
from flask import Flask, jsonify, Response, request
import flask
from cache import MemoryCache
from solve_file.vector_db_utils import vn
import pandas as pd
from all_data import judge_name_
from db_setting.base import Base,engine


app = Flask(__name__, static_url_path='')

# 创建表
Base.metadata.create_all(engine)

# SETUP
cache = MemoryCache()


vn.connect_to_mysql(
    host=Config.MYSQL_HOST,
    dbname=Config.MYSQL_DATABASE, 
    user=Config.MYSQL_USER, 
    password=Config.MYSQL_PASSWORD, 
    port=Config.MYSQL_PORT)

# NO NEED TO CHANGE ANYTHING BELOW THIS LINE
def requires_cache(fields):
    def decorator(f):
        @wraps(f)
        def decorated(*args, **kwargs):
            id = request.args.get('id')

            if id is None:
                return jsonify({"type": "error", "error": "No id provided"})
            
            for field in fields:
                if cache.get(id=id, field=field) is None:
                    return jsonify({"type": "error", "error": f"No {field} found"})
            
            field_values = {field: cache.get(id=id, field=field) for field in fields}
            
            # Add the id to the field_values
            field_values['id'] = id

            return f(*args, **field_values, **kwargs)
        return decorated
    return decorator

@app.route('/api/v0/generate_questions', methods=['GET'])
def generate_questions():
    return jsonify({
        "type": "question_list", 
        "questions": vn.generate_questions(),
        "header": "Here are some questions you can ask:"
        })

@app.route('/api/v0/generate_sql', methods=['GET'])
def generate_sql():
    question = flask.request.args.get('question')

    if question is None:
        return jsonify({"type": "error", "error": "No question provided"})

    id = cache.generate_id(question=question)
    sql = vn.generate_sql(question=question)

    cache.set(id=id, field='question', value=question)
    cache.set(id=id, field='sql', value=sql)

    return jsonify(
        {
            "type": "sql", 
            "id": id,
            "text": sql,
        })

@app.route('/api/v0/run_sql', methods=['GET'])
@requires_cache(['sql'])
def run_sql(id: str, sql: str):
    try:
        df = vn.run_sql(sql=sql)

        cache.set(id=id, field='df', value=df)

        return jsonify(
            {
                "type": "df", 
                "id": id,
                "df": df.head(10).to_json(orient='records'),
            })

    except Exception as e:
        return jsonify({"type": "error", "error": str(e)})

@app.route('/api/v0/download_csv', methods=['GET'])
@requires_cache(['df'])
def download_csv(id: str, df):
    csv = df.to_csv()

    return Response(
        csv,
        mimetype="text/csv",
        headers={"Content-disposition":
                 f"attachment; filename={id}.csv"})

@app.route('/api/v0/generate_plotly_figure', methods=['GET'])
@requires_cache(['df', 'question', 'sql'])
def generate_plotly_figure(id: str, df, question, sql):
    try:
        code = vn.generate_plotly_code(question=question, sql=sql, df_metadata=f"Running df.dtypes gives:\n {df.dtypes}")
        fig = vn.get_plotly_figure(plotly_code=code, df=df, dark_mode=False)
        fig_json = fig.to_json()

        cache.set(id=id, field='fig_json', value=fig_json)

        return jsonify(
            {
                "type": "plotly_figure", 
                "id": id,
                "fig": fig_json,
            })
    except Exception as e:
        # Print the stack trace
        import traceback
        traceback.print_exc()

        return jsonify({"type": "error", "error": str(e)})

@app.route('/api/v0/get_training_data', methods=['GET'])
def get_training_data():
    df = vn.get_training_data()

    return jsonify(
    {
        "type": "df", 
        "id": "training_data",
        "df": df.head(25).to_json(orient='records'),
    })

@app.route('/api/v0/remove_training_data', methods=['POST'])
def remove_training_data():
    # Get id from the JSON body
    id = flask.request.json.get('id')

    if id is None:
        return jsonify({"type": "error", "error": "No id provided"})

    if vn.remove_training_data(id=id):
        return jsonify({"success": True})
    else:
        return jsonify({"type": "error", "error": "Couldn't remove training data"})

@app.route('/api/v0/train', methods=['POST'])
def add_training_data():
    question = flask.request.json.get('question')
    sql = flask.request.json.get('sql')
    ddl = flask.request.json.get('ddl')
    documentation = flask.request.json.get('documentation')

    try:
        id = vn.train(question=question, sql=sql, ddl=ddl, documentation=documentation)

        return jsonify({"id": id})
    except Exception as e:
        print("TRAINING ERROR", e)
        return jsonify({"type": "error", "error": str(e)})

@app.route('/api/v0/generate_followup_questions', methods=['GET'])
@requires_cache(['df', 'question', 'sql'])
def generate_followup_questions(id: str, df, question, sql):
    followup_questions = vn.generate_followup_questions(question=question, sql=sql, df=df)

    cache.set(id=id, field='followup_questions', value=followup_questions)

    return jsonify(
        {
            "type": "question_list", 
            "id": id,
            "questions": followup_questions,
            "header": "Here are some followup questions you can ask:"
        })

@app.route('/api/v0/load_question', methods=['GET'])
@requires_cache(['question', 'sql', 'df', 'fig_json', 'followup_questions'])
def load_question(id: str, question, sql, df, fig_json, followup_questions):
    try:
        return jsonify(
            {
                "type": "question_cache", 
                "id": id,
                "question": question,
                "sql": sql,
                "df": df.head(10).to_json(orient='records'),
                "fig": fig_json,
                "followup_questions": followup_questions,
            })

    except Exception as e:
        return jsonify({"type": "error", "error": str(e)})

@app.route('/api/v0/get_question_history', methods=['GET'])
def get_question_history():
    return jsonify({"type": "question_history", "questions": cache.get_all(field_list=['question']) })

@app.route('/')
def root():
    return app.send_static_file('index.html')

@app.route("/all", methods=['GET'])
def all():
    question = flask.request.args.get('question')

    if question is None:
        return jsonify({"type": "error", "error": "No question provided"})

    id = cache.generate_id(question=question)
    sql = vn.generate_sql(question=question)

    cache.set(id=id, field='question', value=question)
    cache.set(id=id, field='sql', value=sql)

    try:
        df = vn.run_sql(sql=sql)

        cache.set(id=id, field='df', value=df)

        return jsonify(
            {
                "df": df.head(10).to_dict(orient='records'),
            })

    # except Exception as e:
        # return jsonify({"type": "error", "error": str(e)})
    except Exception as e:
        return jsonify('请尝试重新输入问题')
    

@app.route("/upload/file/app", methods=["POST"])
def upload_file_app():
    if "file" not in request.files:
        return jsonify({"error": "No file uploaded"}), 400

    file = request.files["file"]
    if file.filename == "":
        return jsonify({"error": "Empty filename"}), 400

    try:
        # 支持 CSV 或 Excel
        if file.filename.endswith(".csv"):
            df = pd.read_csv(file)
            print("==========文件已经上传============")
            judge_name_(file.filename,df)

        elif file.filename.endswith((".xls", ".xlsx")):
            df = pd.read_excel(file)
            print("==========文件已经上传============")
            judge_name_(file.filename,df)

        else:
            return jsonify({"error": "Unsupported file format"}), 400

        return jsonify({
            "df": df.head(10).to_dict(orient='records'),
        })

    except Exception as e:
        return jsonify({"error": str(e)}), 500


@app.route('/ask/rag', methods=['POST'])
def ask_rag():
    data = flask.request.get_json()
    question = data.get("question") if data else None

    if not question:
        return jsonify({"type": "error", "error": "No question provided"})

    response = vn.ask_with_rag(question, top_k=100)
    return jsonify({"answer": response})


@app.route('/ask/combined', methods=['POST'])
def ask_combined():
    # Get JSON payload
    data = request.get_json()
    question = data.get("question") if data else None

    # Validate question
    if not question:
        return jsonify({"type": "error", "error": "No question provided"})

    # Initialize response dictionary
    response = {}

    # Get RAG answer
    try:
        rag_answer = vn.ask_with_rag(question, top_k=5)
        response["answer"] = rag_answer
    except Exception as e:
        return jsonify({"type": "error", "error": f"RAG processing failed: {str(e)}"})

    # Generate and execute SQL query
    try:
        # Generate cache ID and SQL query
        cache_id = cache.generate_id(question=question)
        sql = vn.generate_sql(question=question)

        # Cache question and SQL
        cache.set(id=cache_id, field='question', value=question)
        cache.set(id=cache_id, field='sql', value=sql)

        # Execute SQL and cache DataFrame
        df = vn.run_sql(sql=sql)
        cache.set(id=cache_id, field='df', value=df)

        # Add top 10 rows of DataFrame to response
        response["df"] = df.head(10).to_dict(orient='records')
    except Exception as e:
        return jsonify({"type": "error", "error": f"SQL execution failed: {str(e)}"})

    return jsonify(response)

if __name__ == '__main__':
    app.run(debug=True)

